package com.authine.cloudpivot.ext.service.export.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapBuilder;
import com.authine.cloudpivot.ext.Enum.*;
import com.authine.cloudpivot.ext.constants.ModelConst;
import com.authine.cloudpivot.ext.dto.model.invoice.InvoiceApply;
import com.authine.cloudpivot.ext.dto.model.invoice.InvoiceDetail;
import com.authine.cloudpivot.ext.dto.model.invoice.InvoiceDetailBill;
import com.authine.cloudpivot.ext.dto.model.report.HistoricalCostReport;
import com.authine.cloudpivot.ext.dto.model.trade.Trade;
import com.authine.cloudpivot.ext.dto.query.FinanceReportQuery;
import com.authine.cloudpivot.ext.dto.query.InvoiceDetailQuery;
import com.authine.cloudpivot.ext.dto.system.OrganizationDto;
import com.authine.cloudpivot.ext.service.export.FinanceReportService;
import com.authine.cloudpivot.ext.utils.BigDecimalUtils;
import com.authine.cloudpivot.ext.utils.JdbcTemplateUtils;
import com.authine.cloudpivot.ext.utils.RegexUtils;
import com.authine.cloudpivot.ext.utils.TaxRateUtils;
import com.authine.cloudpivot.ext.utils.system.OrgUtils;
import com.authine.mvp.app.launcher.domain.utills.ApplicationContextUtils;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Maps;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

public class FinanceReportServiceImpl implements FinanceReportService {
    NamedParameterJdbcTemplate jdbcTemplate =  ApplicationContextUtils.getBean(NamedParameterJdbcTemplate.class);

    @Override
    public List<HistoricalCostReport> costReportData(FinanceReportQuery query) {
        List<HistoricalCostReport> historicalCostList = this.historicalCost(query);
        if (null == historicalCostList) {
            historicalCostList = Collections.emptyList();
        }

        List<String> targetSaleOrderGoodsIds = new ArrayList<>();
        List<String> targetPurchaseOrderGoodsIds = new ArrayList<>();

        Set<String> customerOrgIds = new HashSet<>();
        Set<String> vendorOrgIds = new HashSet<>();
        for (List<InvoiceDetail> invoiceDetails : historicalCostList.stream().map(HistoricalCostReport::getDetailList).collect(Collectors.toList())) {
            //获取销售交易对应的客户组织ID
            customerOrgIds.addAll(invoiceDetails.stream()
                    .filter(t -> TradeDirection.valueOf(t.getTrade().getDirection()) == TradeDirection.Sale)
                    .map(t -> t.getTrade().getOrganization_id().getId()).collect(Collectors.toSet()));

            targetSaleOrderGoodsIds.addAll(invoiceDetails.stream().filter(t -> TradeDirection.valueOf(t.getTrade().getDirection()) == TradeDirection.Sale).map(t -> t.getTrade().getTarget_order_goods_id()).collect(Collectors.toList()));
            targetPurchaseOrderGoodsIds.addAll(invoiceDetails.stream().filter(t -> TradeDirection.valueOf(t.getTrade().getDirection()) == TradeDirection.Purchase).map(t -> t.getTrade().getTarget_order_goods_id()).collect(Collectors.toList()));
            vendorOrgIds.addAll(invoiceDetails.stream().filter(t -> TradeDirection.valueOf(t.getTrade().getDirection()) == TradeDirection.Purchase).map(t -> t.getTrade().getOrganization_id().getId()).collect(Collectors.toSet()));
        }

        //根据销售商品ID获取采购交易
        List<Trade> targetPurchaseTrades = new ArrayList<>();
        if (!targetSaleOrderGoodsIds.isEmpty()) {
            for (List<String> partitionIds : Lists.partition(targetSaleOrderGoodsIds, 500)) {
                List<Trade> tradeList = JdbcTemplateUtils.builder(ModelConst.T_TRADE).selectField("*")
                        .eq("direction", TradeDirection.Purchase.name())
                        .in("order_goods_id", partitionIds)
                        .queryForPOJOList(Trade.class);
                targetPurchaseTrades.addAll(tradeList);
                vendorOrgIds.addAll(targetPurchaseTrades.stream().map(t->t.getOrganization_id().getId()).collect(Collectors.toSet()));
            }
        }

        //根据采购商品ID获取销售交易
        List<Trade> targetSaleTrades = new ArrayList<>();
        if (!targetPurchaseOrderGoodsIds.isEmpty()) {
            for (List<String> partitionIds : Lists.partition(targetPurchaseOrderGoodsIds, 500)) {
                List<Trade> tempTargetSaleTrades = JdbcTemplateUtils.builder(ModelConst.T_TRADE).selectField("*")
                        .eq("direction", TradeDirection.Sale.name())
                        .in("order_goods_id", partitionIds)
                        .queryForList(Trade.class);
                targetSaleTrades.addAll(tempTargetSaleTrades);
            }
            if (!targetSaleTrades.isEmpty()) {
                customerOrgIds.addAll(targetSaleTrades.stream().map(t->t.getOrganization_id().getId()).collect(Collectors.toSet()));
            }
        }

        //原有供应商
        Map<String, List<String>> originVendorOrgIds = new HashMap<>();
        for (String customerOrgId : customerOrgIds) {
            originVendorOrgIds.put(customerOrgId, OrgUtils.listVendorByCustomerId(customerOrgId));
        }

        List<OrganizationDto> virtualOrgList = Collections.emptyList();
        if (!vendorOrgIds.isEmpty()) {
            virtualOrgList = OrgUtils.getOrgListByOrgIds(new ArrayList<>(vendorOrgIds));
            virtualOrgList = virtualOrgList.stream().filter(item->item.getVirtual().booleanValue()).collect(Collectors.toList());
        }

        originVendorOrgIds = this.excludeVirtual(originVendorOrgIds, virtualOrgList);

        this.setupCostAmount(historicalCostList, query.getEndDate(), virtualOrgList, originVendorOrgIds, targetPurchaseTrades, targetSaleTrades);
        this.setupVendorAmount(historicalCostList, targetPurchaseTrades, virtualOrgList, targetSaleTrades, originVendorOrgIds);
        this.setupOther(historicalCostList);

        return historicalCostList;
    }


    private Map<String, List<String>> excludeVirtual(Map<String, List<String>> originVendorOrgIds, List<OrganizationDto> virtualOrgList) {

        if (CollectionUtils.isEmpty(originVendorOrgIds) || CollectionUtils.isEmpty(virtualOrgList)) {

            return originVendorOrgIds;
        }

        Map<String, List<String>> results = new HashMap<>();
        originVendorOrgIds.forEach((key, values) -> {
            List<String> vendorOrgIds = new ArrayList<>();
            for (String vendorOrgId : values) {
                if (virtualOrgList.stream().noneMatch(t -> t.getId().equals(vendorOrgId))) {
                    vendorOrgIds.add(vendorOrgId);
                }
            }
            results.put(key, vendorOrgIds);
        });

        return results;
    }

    public List<HistoricalCostReport> historicalCost(FinanceReportQuery query) {
        List<InvoiceApply> invoiceList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_APPLY).selectField("*")
                .in("JSON_UNQUOTE(sale_organization_id -> '$.id')", query.getSaleOrganizationIds())
                .in("number", StringUtils.isNotBlank(query.getNumber()) ? Arrays.asList(StringUtils.split(query.getNumber(), ":")) : null)
                .in("JSON_UNQUOTE(purchase_organization_id -> '$.id')", query.getOrganizationIds())
                .like("purchase_name", query.getCustomerName())
                .lte("date", query.getEndDate())
                .in("statuss", Arrays.asList(InvoiceStatus.Done.name(), InvoiceStatus.ReceiverConfirming.name()))
                .queryForPOJOList(InvoiceApply.class);

        if (CollectionUtils.isEmpty(invoiceList)) {
            return Collections.emptyList();
        }

        List<InvoiceDetail> invoiceDetailList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL).selectField("*")
                .in("invoice_apply_id", invoiceList.stream().map(InvoiceApply::getId).collect(Collectors.toList()))
                .queryForPOJOList(InvoiceDetail.class);

//        String customerOrgIds = invoiceList.stream().filter(t -> t.getBusiness_type().equals(InvoiceBusinessType.Product.name())
//                || t.getBusiness_type().equals(InvoiceBusinessType.RedProduct.name()))
//                .map(t -> t.getPurchase_organization_id().getId()).distinct().collect(Collectors.joining(","));

        List<HistoricalCostReport> results = new ArrayList<>();
        for (InvoiceApply invoiceApply : invoiceList) {
            List<InvoiceDetail> tempInvoiceDetailList = invoiceDetailList.stream()
                    .filter(t -> t.getInvoice_apply_id().equals(invoiceApply.getId())).collect(Collectors.toList());
            //判断当前发票明细是否不存在转采，然后进行非转采成本数据构建
            List<InvoiceDetail> detailList = new ArrayList<>();
            for (InvoiceDetail invoiceDetail : tempInvoiceDetailList) {
                Trade trade = JdbcTemplateUtils.builder(ModelConst.T_TRADE).selectField("*")
                        .eq("id", invoiceDetail.getTrade_id())
                        .queryForPOJO(Trade.class);
                invoiceDetail.setTrade(trade);
                detailList.add(invoiceDetail);
            }

            results.add(buildNonTransferCostModel(invoiceApply,
                    tempInvoiceDetailList.stream().map(t -> t.getTrade().getCustomer_order_id()).collect(Collectors.joining(",")),
                    detailList));
        }

        this.historicalCostFeeDetail(invoiceList, results);
        return results;
    }
    /**
     * 构建非转采成本数据MODEL
     *
     * @return 非转采成本数据
     */
    private HistoricalCostReport buildNonTransferCostModel(InvoiceApply invoiceApply, String tradeCustomerOrderIdStr, List<InvoiceDetail> detailList) {
        BigDecimal pretaxContractFee = BigDecimal.ZERO;//票面未税金额合计
        if (TradeOrderType.Agent.name().equals(invoiceApply.getTrade_order_type())) {
            pretaxContractFee = invoiceApply.getPrice_amount();
        }

        Integer accountPeriod = 0;
        if (InvoiceBusinessType.Product.name().equals(invoiceApply.getBusiness_type())
                || InvoiceBusinessType.RedProduct.name().equals(invoiceApply.getBusiness_type())) {
            List<Trade> tradeList = new ArrayList<>();
            for (InvoiceDetail invoiceDetail : detailList) {
                Trade trade = invoiceDetail.getTrade();
                tradeList.add(trade);
            }
            tradeList.sort((o1, o2) -> {
                if (DateUtil.parse(o1.getCreatedTime(), DatePattern.NORM_DATETIME_PATTERN).after(DateUtil.parse(o2.getCreatedTime(), DatePattern.NORM_DATETIME_PATTERN))) {
                    return 1;
                } else if (DateUtil.parse(o1.getCreatedTime(), DatePattern.NORM_DATETIME_PATTERN).before(DateUtil.parse(o2.getCreatedTime(), DatePattern.NORM_DATETIME_PATTERN))) {
                    return -1;
                }
                return 0;
            });

            accountPeriod = tradeList.get(tradeList.size() - 1).getAccount_period().intValue();
        }

        return HistoricalCostReport.builder()
                .groupField("成本报表")
                .invoiceApplyId(invoiceApply.getId())
                .customerOrganizationId(invoiceApply.getPurchase_organization_id().getId())
                .invoiceDetailAmount(invoiceApply.getTotal().doubleValue())
                .invoiceApplyTime(DateUtil.format(invoiceApply.getDate(), DatePattern.NORM_DATETIME_PATTERN))
                .organizationCompanyName(invoiceApply.getPurchase_name())
                .invoiceApplyNumber(invoiceApply.getNumber())
                .invoiceApplyPretaxAmount(invoiceApply.getPrice_amount().doubleValue())
                .tradeCustomerOrderIdStr(tradeCustomerOrderIdStr)
                .parentOrganizationCompanyName(null)
                .transferGoodsAmount(invoiceApply.getTotal().doubleValue())
                .transferGoodsPretaxAmount(invoiceApply.getPrice_amount().doubleValue())
                .businessType(InvoiceBusinessType.valueOf(invoiceApply.getBusiness_type()))
                .maoriAmount(0.0)
                .margin(0.0)
                .pretaxContractFee(pretaxContractFee.doubleValue())
                .ifTransferStr("非转采")
                .tradeOrderType(RegexUtils.notNull(invoiceApply.getTrade_order_type())?TradeOrderType.valueOf(invoiceApply.getTrade_order_type()) : null)
                .detailList(detailList)
                //设置平台组织名称
                .platformOrgName(invoiceApply.getSale_organization_name())
                //初始值
                .transferPretaxSaleAmount(0.0)
                .ordinaryContractPretaxSaleAmount(0.0)
                .specialContractPretaxSaleAmount(0.0)
                .platformPretaxSaleAmount(0.0)
                .interestPretaxSaleAmount(0.0)

                .transferPretaxPurchaseAmount(0.0)
                .ordinaryContractPretaxPurchaseAmount(0.0)
                .specialContractPretaxPurchaseAmount(0.0)
                .platformPretaxPurchaseAmount(0.0)
                .interestPretaxPurchaseAmount(0.0)
                .period(accountPeriod)

                .platformVendorAmount(0.0)
                .platformTransferGoodsPretaxAmount(0.0)
                .originTransferGoodsPretaxAmount(0.0)
                .build();
    }
    /**
     * 获取服务费发票明细具体的【服务费、平台费】未税总和分别是多少
     *
     * @param invoiceList 所有发票单集合
     * @param results     返回的结果集
     */
    private void historicalCostFeeDetail(List<InvoiceApply> invoiceList, List<HistoricalCostReport> results) {
        List<String> serviceInvoiceApplyIds = invoiceList
                .stream()
                .filter(t -> TradeOrderType.Ordinary.name().equals(t.getTrade_order_type()))
                .filter(t -> InvoiceBusinessType.Service.name().equals(t.getBusiness_type()) || InvoiceBusinessType.RedService.name().equals(t.getBusiness_type()))
                .map(InvoiceApply::getId).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(serviceInvoiceApplyIds)) {
            return;
        }

        List<InvoiceDetailBill> invoiceDetailBillList = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL_BILL).selectField("*")
                .in("invoice_apply_id", serviceInvoiceApplyIds)
                .queryForPOJOList(InvoiceDetailBill.class);
        if (CollectionUtils.isEmpty(invoiceDetailBillList)) {
            return;
        }

        results.stream().filter(t -> serviceInvoiceApplyIds.contains(t.getInvoiceApplyId())).forEach(t -> {
            List<InvoiceDetailBill> tempInvoiceDetailBillList = invoiceDetailBillList.stream()
                    .filter(i -> i.getInvoice_apply_id().equals(t.getInvoiceApplyId())).collect(Collectors.toList());

            double pretaxInterestFee = 0.0;
            double pretaxPlatformFee = 0.0;
            for (InvoiceDetailBill detail : tempInvoiceDetailBillList) {
                BigDecimal total = BigDecimalUtils.round(TaxRateUtils.calculateTaxBefore(detail.getTotal(), detail.getTax_rate()), 2);
                if (BillType.FinancialFee == BillType.valueOf(detail.getBill_type())) {
                    pretaxInterestFee = BigDecimalUtils.add(pretaxInterestFee, total).doubleValue();
                } else {
                    pretaxPlatformFee = BigDecimalUtils.add(pretaxPlatformFee, total).doubleValue();
                }
            }

            //由于实际开票未税总额可能会出现小数位偏差，下面是为了解决平台费和服务费总和跟开票未税一致问题
            if (pretaxInterestFee != 0 && pretaxPlatformFee != 0) {
                pretaxPlatformFee = BigDecimalUtils.sub(t.getInvoiceApplyPretaxAmount(), pretaxInterestFee).doubleValue();
            } else if (pretaxInterestFee != 0) {
                pretaxInterestFee = t.getInvoiceApplyPretaxAmount();
            } else {
                pretaxPlatformFee = t.getInvoiceApplyPretaxAmount();
            }

            t.setPretaxInterestFee(pretaxInterestFee);
            t.setPretaxPlatformFee(pretaxPlatformFee);

            t.setInterestPretaxSaleAmount(pretaxInterestFee);
            t.setPlatformPretaxSaleAmount(pretaxPlatformFee);
        });

    }

    private void setupCostAmount(List<HistoricalCostReport> historicalCostList,
                                 Date deadline,
                                 List<OrganizationDto> virtualOrgList,
                                 Map<String, List<String>> originVendorOrgIds,
                                 List<Trade> targetPurchaseTrades,
                                 List<Trade> targetSaleTrades) {

        List<String> tradeIds = new ArrayList<>();
        for (List<InvoiceDetail> invoiceDetails : historicalCostList.stream()
                .filter(t -> InvoiceBusinessType.Product.equals(t.getBusinessType())).map(HistoricalCostReport::getDetailList).collect(Collectors.toList())) {

            for (InvoiceDetail invoiceDetail : invoiceDetails) {
                tradeIds.add(invoiceDetail.getTrade_id());
            }
        }

        List<InvoiceDetail> doneInvoiceDetailList = new ArrayList<>();
        if (!tradeIds.isEmpty()) {
            for (List<String> partTradeIds : Lists.partition(tradeIds, 2000)) {
                String sql = "select detail.invoice_apply_id, detail.trade_id from " + JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_DETAIL) + " detail " +
                        " left join " + JdbcTemplateUtils.getTableName(ModelConst.T_TRADE) + " trade on detail.trade_id = trade.id " +
                        " left join " + JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_APPLY) + " apply on apply.id = detail.invoice_apply_id " +
                        " where detail.offset_quantity <> detail.quantity and apply.statuss = :status " +
                        " and detail.trade_id in (:tradeIds) and apply.business_type = :businessType";
                Map<String, Object> param = MapBuilder.create(new HashMap<String, Object>())
                        .put("status", InvoiceStatus.Done.name())
                        .put("businessType", InvoiceBusinessType.Product.name())
                        .put("tradeIds", partTradeIds)
                        .build();
                doneInvoiceDetailList.addAll(JdbcTemplateUtils.queryForPOJOList(sql, param, ModelConst.T_INVOICE_DETAIL, InvoiceDetail.class));
            }

        }

        List<String> specialContractOrgIds = Arrays.asList("3313", "3463", "3462");

        for (HistoricalCostReport costReport : historicalCostList) {
            if (InvoiceBusinessType.Service == costReport.getBusinessType() ||
                    InvoiceBusinessType.RedService == costReport.getBusinessType()) {
                //服务费成本金额为0
                costReport.setTransferGoodsPretaxAmount(0.0);
                continue;
            }

            List<String> tempTradeIds = costReport.getDetailList().stream().map(InvoiceDetail::getTrade_id).collect(Collectors.toList());
            Set<String> doneInvoiceIds = doneInvoiceDetailList.stream()
                    .filter(t -> tempTradeIds.contains(t.getTrade_id())).map(InvoiceDetail::getInvoice_apply_id).collect(Collectors.toSet());

            Map<Object, List<InvoiceDetail>> doneNumByTradeGroup = this.doneNumGroup(costReport, doneInvoiceIds);

            double transferGoodsPretaxAmount = 0.0;

            double platformTransferGoodsPretaxAmount = 0.0;
            double originTransferGoodsPretaxAmount = 0.0;

            double transferPretaxPurchaseAmount = 0.0;
            double ordinaryContractPretaxPurchaseAmount = 0.0;
            double specialContractPretaxPurchaseAmount = 0.0;

            for (InvoiceDetail detail : costReport.getDetailList()) {

                String vendorOrgId;
                String customerOrgId;

                //当前明细的未税金额
                double tempTransferGoodsPretaxAmount;
                if (detail.getTrade().isTransfer_purchase()) {
                    double doneInvoiceNum = 0.0;
                    List<InvoiceDetail> doneNumList = doneNumByTradeGroup.get(detail.getTrade_id());
                    if (null != doneNumList) {
                        for (InvoiceDetail invoiceDetailDone : doneNumList) {
                            double num = BigDecimalUtils.sub(invoiceDetailDone.getQuantity(), invoiceDetailDone.getOffset_quantity()).doubleValue();
                            doneInvoiceNum = BigDecimalUtils.add(num, doneInvoiceNum).doubleValue();
                        }
                    }

                    tempTransferGoodsPretaxAmount = this.getPurchaseCostAmount(detail.getTrade().getOrder_goods_id(), detail.getQuantity(), doneInvoiceNum);

                    // 转采采购金额 先进先出
                    transferGoodsPretaxAmount = BigDecimalUtils.add(transferGoodsPretaxAmount, tempTransferGoodsPretaxAmount).doubleValue();

                } else {
                    tempTransferGoodsPretaxAmount = detail.getAmount().doubleValue();
                    transferGoodsPretaxAmount = BigDecimalUtils.add(transferGoodsPretaxAmount, detail.getAmount()).doubleValue();
                }

                String targetOrderGoodsId = detail.getTrade().getTarget_order_goods_id();
                if (InvoiceBusinessType.Product == costReport.getBusinessType() || InvoiceBusinessType.RedProduct == costReport.getBusinessType()) {
                    Trade trade = targetPurchaseTrades.stream()
                            .filter(t -> t.getOrder_goods_id().equals(targetOrderGoodsId)).findFirst().orElse(null);
                    if (null == trade) {
                        continue;
                    }
                    vendorOrgId = trade.getOrganization_id().getId();
                    customerOrgId = detail.getTrade().getOrganization_id().getId();


                    if (virtualOrgList.stream().anyMatch(t -> t.getId().equals(vendorOrgId))) {
                        if (virtualOrgList.stream().anyMatch(t -> t.getId().equals(vendorOrgId) && t.getName().contains("代购"))) {
                            //特殊代购
                            if (specialContractOrgIds.contains(customerOrgId)) {
                                specialContractPretaxPurchaseAmount = BigDecimalUtils.add(specialContractPretaxPurchaseAmount, tempTransferGoodsPretaxAmount).doubleValue();
                            }
                            //普通代购
                            else {
                                ordinaryContractPretaxPurchaseAmount = BigDecimalUtils.add(ordinaryContractPretaxPurchaseAmount, tempTransferGoodsPretaxAmount).doubleValue();
                            }
                        }
                        //转采
                        else {
                            transferPretaxPurchaseAmount = BigDecimalUtils.add(transferPretaxPurchaseAmount, tempTransferGoodsPretaxAmount).doubleValue();
                        }
                    }


                } else {

                    Trade trade = targetSaleTrades.stream()
                            .filter(t -> t.getOrder_goods_id().equals(targetOrderGoodsId)).findFirst().orElse(null);
                    if (null == trade) {
                        continue;
                    }
                    customerOrgId = trade.getOrganization_id().getId();
                    vendorOrgId = detail.getTrade().getOrganization_id().getId();
                }

                if (originVendorOrgIds.get(customerOrgId).contains(vendorOrgId)) {
                    originTransferGoodsPretaxAmount = BigDecimalUtils.add(tempTransferGoodsPretaxAmount, originTransferGoodsPretaxAmount).doubleValue();
                }
                //平台供应商
                else {
                    if (virtualOrgList.stream().noneMatch(t -> t.getId().equals(vendorOrgId))) {
                        platformTransferGoodsPretaxAmount = BigDecimalUtils.add(tempTransferGoodsPretaxAmount, platformTransferGoodsPretaxAmount).doubleValue();
                    }
                }
            }

            costReport.setTransferGoodsPretaxAmount(transferGoodsPretaxAmount);

            costReport.setPlatformTransferGoodsPretaxAmount(platformTransferGoodsPretaxAmount);
            costReport.setOriginTransferGoodsPretaxAmount(originTransferGoodsPretaxAmount);
            costReport.setTransferPretaxPurchaseAmount(transferPretaxPurchaseAmount);

            costReport.setOrdinaryContractPretaxPurchaseAmount(ordinaryContractPretaxPurchaseAmount);
            costReport.setSpecialContractPretaxPurchaseAmount(specialContractPretaxPurchaseAmount);
        }
    }

    /**
     * 业财一体化按先进先出原则，计算批次单价
     * cloudOrderGoodsId 云平台订单商品id
     * receivedNum   已经收货数量
     * quantityReceived  本次收货数量
     */
    private Double getPurchaseCostAmount(String orderGoodsId, BigDecimal quantityReceived, Double receivedNum){
        Double purchaseNum = 0.0;//采购数量
        Double costAmount = 0.0;//成本金额
        Double loopNum = 0.0;//单次循环采购数量
        if (null != orderGoodsId) {
            String sql = "SELECT G.num,G.pre_price FROM " +
                    " i_zhuancai_t_member_purchase_goods " +
                    " AS G LEFT JOIN i_zhuancai_t_member_purchase_order AS M ON M.ID = G.member_purchase_order_id " +
                    " LEFT JOIN i_zhuancai_t_transfer_order_goods AS T ON T.ID = G.transfer_order_goods_id " +
                    " LEFT JOIN i_zhuancai_t_transfer_order AS o ON o.ID = T.transfer_order_id  " +
                    " WHERE " +
                    " G.deleted = 0  " +
                    " AND M.deleted = 0  " +
                    " AND o.order_type IS NULL  " +
                    " AND M.is_rp <> TRUE  " +
                    " AND M.member_purchase_order_status IN ( 2, 3, 4 )  " +
                    " AND T.cloud_order_goods_id = :orderGoodsId  " +
                    " ORDER BY " +
                    " G.purchase_batch ASC ";
            List<Map<String, Object>> mapList = JdbcTemplateUtils.queryForList(sql, Maps.newHashMap("orderGoodsId", orderGoodsId));
            if (mapList.size() > 0) {
                for (Map<String, Object> goods : mapList) {
                    // 循环采购数量 如果存在已经收货，判断已收货数量是否大于该本次循化商品的采购数量
                    purchaseNum = BigDecimalUtils.add(goods.get("num"), purchaseNum).doubleValue();
                    //如果已收货数量大于本次循环采购数量，累加循环采购数量，跳出循环，说明第一次的成本已经计算完毕
                    loopNum = BigDecimalUtils.add(goods.get("num"), loopNum).doubleValue();
                    if (receivedNum > loopNum) {
                        continue;
                    }
                    //剩余数量 循环累加的采购数量减去已经收货数量，计算这次循化还应收的数量
                    double residueNum = BigDecimalUtils.sub(loopNum, receivedNum).doubleValue();
                    //判断本次收货数量是否小于等于剩余数量，如果小于等于，说明本次循化还能继续计算成本，返回成本跳出
                    if (quantityReceived.doubleValue() <= residueNum) {
                        costAmount = BigDecimalUtils.add(BigDecimalUtils.round(BigDecimalUtils.mul(quantityReceived, goods.get("pre_price")), 2), costAmount).doubleValue();
                        break;
                    } else {
                        //如果大于，继续循环累计成本，已收货数量增加，本次收货数量减少
                        costAmount = BigDecimalUtils.add(BigDecimalUtils.round(BigDecimalUtils.mul(residueNum, goods.get("pre_price")), 2), costAmount).doubleValue();
                        receivedNum = BigDecimalUtils.add(residueNum, receivedNum).doubleValue();
                        quantityReceived = BigDecimalUtils.sub(quantityReceived, residueNum);
                    }
                }
            }
        }
        return costAmount;
    }

    private Map<Object, List<InvoiceDetail>> doneNumGroup(HistoricalCostReport costReport, Set<String> doneInvoiceApplyIds) {
        doneInvoiceApplyIds = null == doneInvoiceApplyIds ? Collections.emptySet() : doneInvoiceApplyIds;

        Map<Object, List<InvoiceDetail>> doneNumByTradeGroup = new HashMap<>(0);
        List<String> tradeIdList = costReport.getDetailList().stream().map(InvoiceDetail::getTrade_id).collect(Collectors.toList());

        if (InvoiceBusinessType.Product == costReport.getBusinessType()) {
            doneInvoiceApplyIds = doneInvoiceApplyIds.stream().filter(t -> !t.equals(costReport.getInvoiceApplyId())).collect(Collectors.toSet());
            String sql = "SELECT business_id FROM "+ JdbcTemplateUtils.getTableName(ModelConst.T_RECORD_VOUCHER)+" WHERE deleted = 0 AND status = 'PlatformCustomerInvoice' " +
                    "AND business_id IN (:businessIds) AND TRIM(voucher_id) <> '' AND voucher_id NOT NULL";
            List<String> businessIds = jdbcTemplate.queryForList(sql, Maps.newHashMap("businessIds", doneInvoiceApplyIds), String.class);
            if (!CollectionUtils.isEmpty(businessIds)) {
                //从待收和已开票ID列表获取已生成凭证的ID列表
                doneInvoiceApplyIds = doneInvoiceApplyIds.stream().filter(t -> businessIds.contains(String.valueOf(t))).collect(Collectors.toSet());
                //根据当前交易记录和已生成凭证的发票ID列表获取对应的发票详情列表
                List<InvoiceDetail> invoiceDetailList = new ArrayList<>();
                for (List<String> partTradeIds : Lists.partition(tradeIdList, 2000)) {
                    List<InvoiceDetail> invoiceDetails = JdbcTemplateUtils.builder(ModelConst.T_INVOICE_DETAIL).selectField("*")
                            .in("invoice_apply_id", doneInvoiceApplyIds)
                            .in("trade_id", partTradeIds)
                            .queryForPOJOList(InvoiceDetail.class);
                    invoiceDetailList.addAll(invoiceDetails);
                }

                //根据交易记录ID分组
                doneNumByTradeGroup = invoiceDetailList.stream().collect(Collectors.groupingBy(InvoiceDetail::getTrade_id));
            }

        } else {
            List<InvoiceDetail> invoiceDetailList = new ArrayList<>();
            for (List<String> partTradeIds : Lists.partition(tradeIdList, 2000)) {
                String sql = "select detail.* from "+ JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_DETAIL)+" detail " +
                        " left join "+ JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_APPLY)+ " apply on apply.id = detail.invoice_apply_id "+
                        " where apply.statuss <> 'Cancel' and detail.offset_quantity <> detail.quantity and detail.trade_id in (:tradeIds)";
                List<InvoiceDetail> tempInvoiceDetailList = JdbcTemplateUtils.queryForPOJOList(sql, Maps.newHashMap("tradeIds", partTradeIds), JdbcTemplateUtils.getTableName(ModelConst.T_INVOICE_DETAIL), InvoiceDetail.class);
                invoiceDetailList.addAll(tempInvoiceDetailList);
            }

            Set<String> redInvoiceIds = invoiceDetailList.stream().map(InvoiceDetail::getInvoice_apply_id).collect(Collectors.toSet());

            if (!redInvoiceIds.isEmpty()) {
                redInvoiceIds = redInvoiceIds.stream().filter(t -> !t.equals(costReport.getInvoiceApplyId())).collect(Collectors.toSet());
                Set<String> convertDoneInvoiceApplyIds = redInvoiceIds.stream().map(String::valueOf).collect(Collectors.toSet());

                String sql = "SELECT business_id FROM "+ JdbcTemplateUtils.getTableName(ModelConst.T_RECORD_VOUCHER)+" WHERE deleted = 0 AND status = 'PlatformCustomerInvoice' " +
                        "AND business_id IN (:businessIds) AND TRIM(voucher_id) <> '' AND voucher_id NOT NULL";
                List<String> businessIds = jdbcTemplate.queryForList(sql, Maps.newHashMap("businessIds", convertDoneInvoiceApplyIds), String.class);

                //如果返回NULL或者为空，说明这批业务编号没有生成凭证
                if (null != businessIds && !businessIds.isEmpty()) {
                    //从待收和已开票ID列表获取已生成凭证的明细列表
                    List<InvoiceDetail> invoiceDetail = invoiceDetailList.stream().filter(t -> businessIds.contains(String.valueOf(t.getInvoice_apply_id()))).collect(Collectors.toList());
                    //根据当前交易记录和已生成凭证的发票ID列表获取对应的发票详情列表
                    //根据交易记录ID分组
                    doneNumByTradeGroup = invoiceDetail.stream().collect(Collectors.groupingBy(InvoiceDetail::getTrade_id));
                }
            }
        }

        return doneNumByTradeGroup;
    }

    private void setupVendorAmount(List<HistoricalCostReport> historicalCostList,
                                   List<Trade> targetPurchaseTrades,
                                   List<OrganizationDto> virtualOrgList,
                                   List<Trade> targetSaleTrades,
                                   Map<String, List<String>> originVendorOrgIds) {


        //特殊代购组织ID：凯科、志豪
        List<String> specialContractOrgIds = Arrays.asList("3313", "3463", "3462");


        for (HistoricalCostReport costReport : historicalCostList) {

            double originAmount = 0.0;
            double platformAmount = 0.0;

            double transferPretaxSaleAmount = 0.0;
            double ordinaryContractPretaxSaleAmount = 0.0;
            double specialContractPretaxSaleAmount = 0.0;

            for (InvoiceDetail detail : costReport.getDetailList()) {
                String vendorOrgId;
                String customerOrgId;
                String targetOrderGoodsId = detail.getTrade().getTarget_order_goods_id();
                if (InvoiceBusinessType.Product == costReport.getBusinessType() || InvoiceBusinessType.RedProduct == costReport.getBusinessType()) {
                    Trade trade = targetPurchaseTrades.stream()
                            .filter(t -> t.getOrder_goods_id().equals(targetOrderGoodsId)).findFirst().orElse(null);
                    if (null == trade) {
                        continue;
                    }
                    vendorOrgId = trade.getOrganization_id().getId();
                    customerOrgId = detail.getTrade().getOrganization_id().getId();

                    if (virtualOrgList.stream().anyMatch(t -> t.getId().equals(vendorOrgId))) {
                        if (virtualOrgList.stream().anyMatch(t -> t.getId().equals(vendorOrgId) && t.getName().contains("代购"))) {
                            //特殊代购
                            if (specialContractOrgIds.contains(customerOrgId)) {
                                specialContractPretaxSaleAmount = BigDecimalUtils.add(specialContractPretaxSaleAmount, detail.getAmount()).doubleValue();
                            }
                            //普通代购
                            else {
                                ordinaryContractPretaxSaleAmount = BigDecimalUtils.add(ordinaryContractPretaxSaleAmount, detail.getAmount()).doubleValue();
                            }
                        }
                        //转采
                        else {
                            transferPretaxSaleAmount = BigDecimalUtils.add(transferPretaxSaleAmount, detail.getAmount()).doubleValue();
                        }
                    }

                    if (originVendorOrgIds.get(customerOrgId).contains(vendorOrgId)) {
                        originAmount = BigDecimalUtils.add(detail.getAmount(), originAmount).doubleValue();
                    }
                    //平台供应商
                    else {
                        if (virtualOrgList.stream().noneMatch(t -> t.getId().equals(vendorOrgId))) {
                            platformAmount = BigDecimalUtils.add(detail.getAmount(), platformAmount).doubleValue();
                        }
                    }

                }
//                else {
//                    vendorOrgId = detail.getTrade().getOrganization_id().getId();
//                    Trade trade = targetSaleTrades.stream()
//                            .filter(t -> t.getOrder_goods_id().equals(targetOrderGoodsId)).findFirst().orElse(null);
//                    if (null == trade) {
//                        continue;
//                    }
//                    customerOrgId = trade.getOrganization_id().getId();
//                }


            }

            costReport.setOriginVendorAmount(originAmount);
            costReport.setPlatformVendorAmount(platformAmount);

            costReport.setTransferPretaxSaleAmount(transferPretaxSaleAmount);
            costReport.setOrdinaryContractPretaxSaleAmount(ordinaryContractPretaxSaleAmount);
            costReport.setSpecialContractPretaxSaleAmount(specialContractPretaxSaleAmount);
        }
    }

    private void setupOther(List<HistoricalCostReport> historicalCostList) {
        List<InvoiceBusinessType> redBusinessTypes = Arrays.asList(InvoiceBusinessType.RedProduct, InvoiceBusinessType.RedService);

        for (HistoricalCostReport costReport : historicalCostList) {
            costReport.setType(TradeOrderType.Ordinary == costReport.getTradeOrderType()
                    ? costReport.getBusinessType() == InvoiceBusinessType.Product || costReport.getBusinessType() == InvoiceBusinessType.RedProduct ? "云平台货款" : "云平台服务费" : "云平台代购货款");
            //毛利未税：开票金额（未税）-成本金额（未税）
            double maoriAmount = BigDecimalUtils.sub(costReport.getInvoiceApplyPretaxAmount(), costReport.getTransferGoodsPretaxAmount()).doubleValue();
            costReport.setMaoriAmount(maoriAmount);

            if (null == costReport.getPretaxPlatformFee()) {
                costReport.setPretaxPlatformFee(0.0);
            }

            if (null == costReport.getPretaxInterestFee()) {
                costReport.setPretaxInterestFee(0.0);
            }
            //货物价差:公式计算：总毛利（未税）-代购服务费-平台费-服务费
            double priceDifference = BigDecimalUtils.sub(BigDecimalUtils.sub(BigDecimalUtils.sub(maoriAmount, costReport.getPretaxContractFee()), costReport.getPretaxPlatformFee()), costReport.getPretaxInterestFee()).doubleValue();
            costReport.setPriceDifference(priceDifference);

            // 毛利率:公式计算：IF(货物价差=0,"0%",货物价差/开票金额（未税）)
            double margin = 0.0;
            if (Double.compare(priceDifference, 0.0) != 0) {
                margin = BigDecimalUtils.div(priceDifference, costReport.getInvoiceApplyPretaxAmount()).doubleValue();
            }
            costReport.setMargin(margin);

            //平台供应商平台费:公式计算：平台供应商（不含客户原有供应商和不含虚拟供应商）供货含税金额*0.03/1.06
            double platformVendorPretaxPlatformFee = BigDecimalUtils.round(BigDecimalUtils.div(BigDecimalUtils.mul(costReport.getPlatformVendorAmount(), 0.03), 1.06), 2).doubleValue();
            costReport.setPlatformVendorPretaxPlatformFee(platformVendorPretaxPlatformFee);

            //利润合计
            costReport.setTotalProfit(BigDecimalUtils.add(platformVendorPretaxPlatformFee, priceDifference).doubleValue());

            costReport.setPlatformVendorGross(BigDecimalUtils.sub(costReport.getPlatformVendorAmount(), costReport.getPlatformTransferGoodsPretaxAmount()).doubleValue());
            costReport.setOriginVendorGross(BigDecimalUtils.sub(costReport.getOriginVendorAmount(), costReport.getOriginTransferGoodsPretaxAmount()).doubleValue());
            costReport.setTransferVendorGross(BigDecimalUtils.sub(costReport.getTransferPretaxSaleAmount(), costReport.getTransferPretaxPurchaseAmount()).doubleValue());
            costReport.setOrdinaryContractGross(BigDecimalUtils.sub(costReport.getOrdinaryContractPretaxSaleAmount(), costReport.getOrdinaryContractPretaxPurchaseAmount()).doubleValue());
            costReport.setSpecialContractGross(BigDecimalUtils.sub(costReport.getSpecialContractPretaxSaleAmount(), costReport.getSpecialContractPretaxPurchaseAmount()).doubleValue());
            costReport.setPlatformFeeGross(BigDecimalUtils.sub(costReport.getPlatformPretaxSaleAmount(), costReport.getPlatformPretaxPurchaseAmount()).doubleValue());
            costReport.setInterestFeeGross(BigDecimalUtils.sub(costReport.getInterestPretaxSaleAmount(), costReport.getInterestPretaxPurchaseAmount()).doubleValue());
            if (costReport.getBusinessType() == InvoiceBusinessType.Service || costReport.getBusinessType() == InvoiceBusinessType.RedService) {
                costReport.setOrdinaryGrossMargin(0.00);
            } else if (costReport.getPeriod() == 0) {
                costReport.setOrdinaryGrossMargin(0.03);
            } else {
                costReport.setOrdinaryGrossMargin(BigDecimalUtils.round(BigDecimalUtils.div(BigDecimalUtils.mul(BigDecimalUtils.div(costReport.getPeriod().doubleValue(), 30), 1.5).add(new BigDecimal("3")), 100), 4).doubleValue());
            }

            if (costReport.getOrdinaryContractPretaxSaleAmount() == 0.0) {
                costReport.setContractGrossMargin(0.0);
            } else {
                costReport.setContractGrossMargin(BigDecimalUtils.round(BigDecimalUtils.div(BigDecimalUtils.sub(costReport.getOrdinaryContractPretaxSaleAmount(), costReport.getOrdinaryContractPretaxPurchaseAmount()), costReport.getOrdinaryContractPretaxSaleAmount()), 4).doubleValue());
            }
            if (costReport.getTransferPretaxSaleAmount() == 0.0) {
                costReport.setTransferGrossMargin(0.0);
            } else {
                costReport.setTransferGrossMargin(BigDecimalUtils.round(BigDecimalUtils.div(BigDecimalUtils.sub(costReport.getTransferPretaxSaleAmount(), costReport.getTransferPretaxPurchaseAmount()), costReport.getTransferPretaxSaleAmount()), 4).doubleValue());
            }

            double bonusSkillBase1 = costReport.getContractGrossMargin() < costReport.getOrdinaryGrossMargin() ?
                    BigDecimalUtils.div(BigDecimalUtils.mul(costReport.getOrdinaryContractPretaxSaleAmount(),
                            costReport.getContractGrossMargin()), costReport.getOrdinaryGrossMargin()).doubleValue() : costReport.getOrdinaryContractPretaxSaleAmount().doubleValue();

            double bonusSkillBase2 = costReport.getTransferGrossMargin() < costReport.getOrdinaryGrossMargin() ?
                    BigDecimalUtils.div(BigDecimalUtils.mul(costReport.getTransferPretaxSaleAmount(),
                            costReport.getTransferGrossMargin()), costReport.getOrdinaryGrossMargin()).doubleValue() : costReport.getTransferPretaxSaleAmount().doubleValue();


            double bonusSkillBase = BigDecimalUtils.round(BigDecimalUtils.add(BigDecimalUtils.add(bonusSkillBase1, bonusSkillBase2), costReport.getPlatformVendorAmount()), 2).doubleValue();

            costReport.setBonusSkillBase(bonusSkillBase);

            if (redBusinessTypes.contains(costReport.getBusinessType())) {
                costReport.setInvoiceDetailAmount(-costReport.getInvoiceDetailAmount());
                costReport.setInvoiceApplyPretaxAmount(-costReport.getInvoiceApplyPretaxAmount());
                costReport.setTransferGoodsPretaxAmount(-costReport.getTransferGoodsPretaxAmount());
                costReport.setMaoriAmount(-costReport.getMaoriAmount());
                costReport.setPlatformVendorAmount(-costReport.getPlatformVendorAmount());
                costReport.setOriginVendorAmount(-costReport.getOriginVendorAmount());
                costReport.setPretaxPlatformFee(-costReport.getPretaxPlatformFee());
                costReport.setPretaxInterestFee(-costReport.getPretaxInterestFee());
                costReport.setPretaxContractFee(-costReport.getPretaxContractFee());
                costReport.setPriceDifference(-costReport.getPriceDifference());
                costReport.setTotalProfit(-costReport.getTotalProfit());
                costReport.setPlatformVendorPretaxPlatformFee(-costReport.getPlatformVendorPretaxPlatformFee());

                costReport.setTransferPretaxSaleAmount(-costReport.getTransferPretaxSaleAmount());
                costReport.setOrdinaryContractPretaxSaleAmount(-costReport.getOrdinaryContractPretaxSaleAmount());
                costReport.setSpecialContractPretaxSaleAmount(-costReport.getSpecialContractPretaxSaleAmount());
                costReport.setPlatformPretaxSaleAmount(-costReport.getPlatformPretaxSaleAmount());
                costReport.setInterestPretaxSaleAmount(-costReport.getInterestPretaxSaleAmount());
                costReport.setPlatformTransferGoodsPretaxAmount(-costReport.getPlatformTransferGoodsPretaxAmount());
                costReport.setOriginTransferGoodsPretaxAmount(-costReport.getOriginTransferGoodsPretaxAmount());
                costReport.setTransferPretaxPurchaseAmount(-costReport.getTransferPretaxPurchaseAmount());
                costReport.setOrdinaryContractPretaxPurchaseAmount(-costReport.getOrdinaryContractPretaxPurchaseAmount());
                costReport.setSpecialContractPretaxPurchaseAmount(-costReport.getSpecialContractPretaxPurchaseAmount());
                costReport.setPlatformPretaxPurchaseAmount(-costReport.getPlatformPretaxPurchaseAmount());
                costReport.setInterestPretaxPurchaseAmount(-costReport.getInterestPretaxPurchaseAmount());
                costReport.setInterestPretaxPurchaseAmount(-costReport.getInterestPretaxPurchaseAmount());
                costReport.setMaoriAmount(-costReport.getMaoriAmount());
                costReport.setPlatformVendorGross(-costReport.getPlatformVendorGross());
                costReport.setOriginVendorGross(-costReport.getOriginVendorGross());
                costReport.setTransferVendorGross(-costReport.getTransferVendorGross());
                costReport.setOrdinaryContractGross(-costReport.getOrdinaryContractGross());
                costReport.setSpecialContractGross(-costReport.getSpecialContractGross());
                costReport.setPlatformFeeGross(-costReport.getPlatformFeeGross());
                costReport.setInterestFeeGross(-costReport.getInterestFeeGross());
            }
        }
    }
}
